package org.bing.engine.common.process;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import org.bing.engine.common.logging.Log;
import org.bing.engine.common.logging.LogFactory;

public class ProcessManager {
    private static final Log logger = LogFactory.getLog(ProcessManager.class);
    private static final Map<String, Process> map = new HashMap<String, Process>();

    /**
     * Notice: don't read process.stdstream and errstream, target process redirect std/err first.
     */
    public synchronized String start(String guid, String home, String[] cmd) {
        ProcessBuilder pb = new ProcessBuilder(cmd);
        if (home != null && new File(home).exists()) {
            pb.directory(new File(home));
        }
        try {
            Process process = pb.start();
            if ("true".equalsIgnoreCase(System.getProperty("engine.debug"))) {
                debug(process);
            }
            sleep(1000);
            try {
                int ev = process.exitValue();
                if (ev == 0) {
                    logger.info("Process is normal terminaled.");
                } else {
                    logger.error("Process is error terminaled! " + ev);
                }
                process.destroy();
                return null;
            } catch (IllegalThreadStateException e) {
                logger.debug("Process is running. " + guid);
                map.put(guid, process);
                return guid;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public synchronized void destroy(String guid) {
        Process process = map.remove(guid);
        if (process != null) {
            // logger.info("Ready to destory " + guid);
            process.destroy();
        }
    }

    public synchronized boolean exists(String guid) {
        return map.containsKey(guid);
    }

    private void debug(Process proc) {
        copyInThread(proc.getErrorStream(), System.err);
        copyInThread(proc.getInputStream(), System.out);
        copyInThread(System.in, proc.getOutputStream());
    }

    private void copyInThread(final InputStream in, final OutputStream out) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                try {
                    byte[] buf = new byte[1024];
                    int len = in.read(buf);
                    while (len > 0) {
                        out.write(buf, 0, len);
                        len = in.read(buf);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        });
        t.setDaemon(true);
        t.start();
    }

    private void sleep(int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            // ignore
        }
    }
}
